/*******************************************
 * Copyright 2007 Aphrodite Framework for javaME .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * Email : owner@aphroditeme.dev.java.net
 * Contributors:
 *     Kent Elton - initial API and implementation
 *******************************************/

package com.aphrodite.util.math;

public abstract class MathEngine {

	private static int _digits = 8;

	private static long _dmul = 0x5f5e100L;

	private static int _fbits = 24;

	private static long _flt = 0L;

	private static long _fmask = 0xffffffL;

	private static long _one;

	private static long _pi;

	private static long e[];

	public static double E = 2.718281828459045;

	public static final long MAX_VALUE = 0x7fffffffffffffffL;

	public static final long MIN_VALUE = 0x8000000000000001L;

	public static double PI = 3.141592653589793;

	static {
		_one = 0x1000000L;
		_pi = 0x3243f6aL;
		e = (new long[] { _one, 0x2b7e151L, 0x763992eL, 0x1415e5bfL, 0x3699205cL });
		PI = _pi;
		E = e[1];
	}

	public static long abs(long l) {
		if (l < 0L)
			return -l;
		else
			return l;
	}

	public static long acos(long l) {
		return (long) PI / 2L - asin(l);
	}

	public static long asin(long l) {
		if (abs(l) > _one) {
			throw new ArithmeticException("Bad Input");
		} else {
			boolean flag = l < 0L;
			l = abs(l);
			long l1 = mul(mul(mul(mul(0x236cf >> (int) _flt, l) - (0x92748 >> (int) _flt), l)
					+ (0x15acb4 >> (int) _flt), l)
					- (0x36d0dd >> (int) _flt), l)
					+ (0x1921f27 >> (int) _flt);
			long l2 = (long) PI / 2L - mul(sqrt(_one - l), l1);
			return flag ? -l2 : l2;
		}
	}

	public static long atan(long l) {
		return asin(div(l, sqrt(_one + mul(l, l))));
	}

	public static long atan2(long l, long l1) {
		long l2 = 0L;
		if (l1 > 0L)
			l2 = atan(div(l, l1));
		else if (l1 < 0L) {
			l2 = (long) (l1 >= 0L ? PI : -PI) - atan(abs(div(l, l1)));
		} else {
			if (l1 == 0L && l == 0L)
				throw new ArithmeticException("Bad Input");
			l2 = (long) (l >= 0L ? PI : -PI) / 2L;
		}
		return l2;
	}

	public static long convert(long l, int i) {
		long l1 = l >= 0L ? 1L : -1L;
		if (abs(i) < 25L)
			if (_fbits < i)
				l = l + l1 * (1L << (i - _fbits >> 1)) >> i - _fbits;
			else
				l <<= _fbits - i;
		return l;
	}

	public static long cos(long l) {
		return sin((long) (PI / 2L - l));
	}

	public static long cot(long l) {
		return div(cos(l), sin(l));
	}

	public static long div(long l, long l1) {
		boolean flag = false;
		int i = _fbits;
		if (l1 == _one)
			return l;
		if ((l1 & _fmask) == 0L)
			return l / (l1 >> i);
		if (l < 0L && l1 > 0L || l > 0L && l1 < 0L)
			flag = true;
		if (l < 0L)
			l = -l;
		if (l1 < 0L)
			l1 = -l1;
		for (; Math.max(l, l1) >= 1L << 63 - i; i--) {
			l >>= 1;
			l1 >>= 1;
		}

		long l2 = (l << i) / l1 << _fbits - i;
		return flag ? -l2 : l2;
	}

	public static long exp(long l) {
		if (l == 0L)
			return _one;
		boolean flag = l < 0L;
		l = abs(l);
		int i = (int) (l >> _fbits);
		long l1 = _one;
		for (int j = 0; j < i / 4; j++)
			l1 = mul(l1, e[4] >> (int) _flt);

		if (i % 4 > 0)
			l1 = mul(l1, e[i % 4] >> (int) _flt);
		l &= _fmask;
		if (l > 0L) {
			long l2 = _one;
			long l3 = 0L;
			long l4 = 1L;
			for (int k = 0; k < 16; k++) {
				l3 += l2 / l4;
				l2 = mul(l2, l);
				l4 *= k + 1;
				if (l4 > l2 || l2 <= 0L || l4 <= 0L)
					break;
			}

			l1 = mul(l1, l3);
		}
		if (flag)
			l1 = div(_one, l1);
		return l1;
	}

	public static int getPrecision() {
		return _fbits;
	}

	public static long log(long l) {
		if (l <= 0L)
			throw new ArithmeticException("Bad Input l is less than or equal to 0");
		long l1 = 0L;
		// long l2 = 0L;
		int i;
		for (i = 0; l >= _one << 1; i++)
			l >>= 1;

		long l4 = (long) i * (long) (0xb17218 >> (int) _flt);
		long l5 = 0L;
		if (l < _one)
			return -log(div(_one, l));
		l -= _one;
		for (int j = 1; j < 20; j++) {
			long l3;
			if (l1 == 0L)
				l3 = l;
			else
				l3 = mul(l1, l);
			if (l3 == 0L)
				break;
			l5 += ((j % 2 != 0 ? 1L : -1L) * l3) / j;
			l1 = l3;
		}

		return l4 + l5;
	}

	public static long mul(long l, long l1) {
		boolean flag = false;
		int i = _fbits;
		long l2 = _fmask;
		if ((l & l2) == 0L)
			return (l >> i) * l1;
		if ((l1 & l2) == 0L)
			return l * (l1 >> i);
		if (l < 0L && l1 > 0L || l > 0L && l1 < 0L)
			flag = true;
		if (l < 0L)
			l = -l;
		if (l1 < 0L)
			l1 = -l1;
		for (; Math.max(l, l1) >= 1L << 63 - i; i--) {
			l >>= 1;
			l1 >>= 1;
			l2 >>= 1;
		}

		long l3 = (l >> i) * (l1 >> i) << i;
		long l4 = (l & l2) * (l1 & l2) >> i;
		l4 += (l & ~l2) * (l1 & l2) >> i;
		l3 = l3 + l4 + ((l & l2) * (l1 & ~l2) >> i) << _fbits - i;
		if (l3 < 0L)
			throw new ArithmeticException("Overflow");
		else
			return flag ? -l3 : l3;
	}

	public static long pow(long l, long l1) {
		boolean flag = l1 < 0L;
		long l2 = _one;
		l1 = abs(l1);
		for (int i = (int) l1 >> _fbits; i-- > 0;)
			l2 = mul(l2, l);

		if (l2 < 0L)
			throw new ArithmeticException("Overflow");
		if (l != 0L)
			l2 = mul(l2, exp(mul(log(l), l1 & _fmask)));
		else
			l2 = 0L;
		if (flag)
			return div(_one, l2);
		else
			return l2;
	}

	public static long round(long l, int i) {
		long l1 = 10L;
		for (int j = 0; j < i; j++)
			l1 *= 10L;

		l1 = div(toFP(5L), toFP(l1));
		if (l < 0L)
			l1 = -l1;
		return l + l1;
	}

	public static int setPrecision(int i) {
		if (i > 24 || i < 0)
			return _digits;
		_fbits = i;
		_one = 1L << i;
		_flt = 24 - i;
		_digits = 0;
		_dmul = 1L;
		_fmask = _one - 1L;
		PI = _pi >> (int) _flt;
		E = e[1] >> (int) _flt;
		for (long l = _one; l != 0L;) {
			l /= 10L;
			_digits++;
			_dmul *= 10L;
		}

		return _digits;
	}

	public static long sin(long l) {
		long l1 = mul(l, div(toFP(180L), (long) PI));
		l1 %= toFP(360L);
		if (l1 < 0L)
			l1 = toFP(360L) + l1;
		long l2 = l1;
		if (l1 >= toFP(90L) && l1 < toFP(270L))
			l2 = toFP(180L) - l1;
		else if (l1 >= toFP(270L) && l1 < toFP(360L))
			l2 = -(toFP(360L) - l1);
		long l3 = l2 / 90L;
		long l4 = mul(l3, l3);
		long l5 = mul(mul(mul(mul(0xfffffffffffee21aL >> (int) _flt, l4) + (0x14594dL >> (int) _flt), l4)
				- (0xa55b13L >> (int) _flt), l4)
				+ (0x1921f9c >> (int) _flt), l3);
		return l5;
	}

	public static long sqrt(long l) {
		return sqrt(l, 24);
	}

	public static long sqrt(long l, int i) {
		if (l < 0L)
			throw new ArithmeticException("Bad Input");
		if (l == 0L)
			return 0L;
		long l1 = l + _one >> 1;
		for (int j = 0; j < i; j++)
			l1 = l1 + div(l, l1) >> 1;

		if (l1 < 0L)
			throw new ArithmeticException("Overflow");
		else
			return l1;
	}

	public static long tan(long l) {
		return div(sin(l), cos(l));
	}

	public static long toFP(long l) {
		return l << _fbits;
	}

	public static long toFP(String s) {
		int i = 0;
		if (s.charAt(0) == '-')
			i = 1;
		String s1 = "-1";
		int j = s.indexOf('.');
		if (j >= 0) {
			for (s1 = s.substring(j + 1, s.length()); s1.length() < _digits; s1 = s1 + "0")
				;
			if (s1.length() > _digits)
				s1 = s1.substring(0, _digits);
		} else {
			j = s.length();
		}
		long l = 0L;
		if (i != j)
			l = Long.parseLong(s.substring(i, j));
		long l1 = Long.parseLong(s1) + 1L;
		long l2 = (l << _fbits) + (l1 << _fbits) / _dmul;
		if (i == 1)
			l2 = -l2;
		return l2;
	}

	public static long toLong(long l) {
		if (l < 0L)
			return -(round(-l, 0) >> _fbits);
		else
			return round(l, 0) >> _fbits;
	}

	public static String toString(long l, int i) {
		if (i > _digits)
			i = _digits;
		String s = Long.toString(round(l, i));
		return s.substring(0, (s.length() - _digits) + i);
	}

	public MathEngine() {}
}
